/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// OpenGL ES 2.0 code

#include <jni.h>
#include <android/log.h>

#include <complex>

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)



extern "C" {
	JNIEXPORT jfloatArray JNICALL Java_org_jfract_util_PictureProcessor_computeDelta(JNIEnv * env, jobject obj,  jfloat xMin, jfloat deltaX, jfloat yMin, jfloat deltaY, jint width, jint startHeight,jint endHeight);
	//JNIEXPORT jintArray JNICALL Java_fractals_Mandelbrot_compute(JNIEnv * env, jobject obj, jfloatArray values, jint maxIter, jint interColorLength);
	JNIEXPORT jintArray JNICALL Java_org_fractals_Mandelbrot_compute(JNIEnv * env, jobject obj, jfloatArray values, jint maxIter, jint interColorLength, jintArray alreadyComputed, jobject listener, jint delta, jint width, jint height,  jint startHeightIndex, jint endHeightIndex, jint type);
};

//private native float[] computeDelta(float xMin, float deltaX, float yMin, float deltaY, int width, int startHeight,int endHeight);
JNIEXPORT jfloatArray JNICALL Java_org_jfract_util_PictureProcessor_computeDelta(JNIEnv * env, jobject obj,  jfloat xMin, jfloat deltaX, jfloat yMin, jfloat deltaY, jint width, jint startHeight,jint endHeight)
{
	jfloatArray result;
	jsize size = (endHeight - startHeight) * width * 2;
	result = env->NewFloatArray(size);

	float fill[size];
	int index = 0;

	for (int j=startHeight;j<endHeight;j++)
	{
		for (int i=0;i<width;i++)
		{
			fill[index++] = xMin + (jfloat)i*deltaX;
			fill[index++] = yMin + (jfloat)j*deltaY;
		}
	}
	env->SetFloatArrayRegion(result, 0, size, fill);
	env->ReleaseFloatArrayElements(result, fill, 0);
	return result;


}

void inline unitMandelBrot(jfloat *valuesNative, int indexForValues, jint maxIter, int internCount, jint interColorLength, int & returnValue, int type)
{
	unsigned int iterations;
	std::complex<float> z = 0;
    std::complex<float> c(valuesNative[indexForValues], valuesNative[indexForValues + 1]);
//    float zx = 0;
//    float zy = 0;
//    float cx = valuesNative[indexForValues];
//    float cy = valuesNative[indexForValues + 1];
//    float tmp = 0;

//    while ((zx*zx+zy*zy)<4.0){
//    	++iterations;
//    	tmp=zx*zx-zy*zy+cx;
//    	zy=2.0*zx*zy+cy;
//    	zx=tmp;
//    }
switch(type)
{	
	case 0:
    for(iterations = 0;iterations < maxIter && std::abs(z) < 2.0;++iterations)
        z = z * z + c;
	break;
	case 1:
    for(iterations = 0;iterations < maxIter && std::abs(z) < 2.0;++iterations)
        z = z * z * z + c;
	break;
	case 2:
    for(iterations = 0;iterations < maxIter && std::abs(z) < 2.0;++iterations)
        z = z * z * z * z + c;
	break;
	case 3:
    for(iterations = 0;iterations < maxIter && std::abs(z) < 2.0;++iterations)
        z = z * z * z * z * z + c;
	break;
	case 4:
    for(iterations = 0;iterations < maxIter && std::abs(z) < 2.0;++iterations)
        z = z * z * z * z * z * z + c;
	break;
}

    if(iterations == maxIter){
        float div = 2 / 30;
        float lastModule = std::abs(z);
        if(lastModule <= div){
            internCount = 0;
        }else{
            float factor = (float)((lastModule - div)) / (float)((2 - div));
            internCount = (int)((factor * interColorLength)) % interColorLength;
        }
        returnValue = maxIter + internCount;
    }
    else
        returnValue = iterations;

}
//protected native int[] compute(float[] values, int maxIter, int interColorLength);
JNIEXPORT jintArray JNICALL Java_org_fractals_Mandelbrot_compute(JNIEnv * env, jobject obj, jfloatArray values, jint maxIter, jint interColorLength, jintArray alreadyComputed, jobject listener, jint delta, jint width, jint height,  jint startHeightIndex, jint endHeightIndex, jint type)
{
	jintArray result;

	jsize size = env->GetArrayLength(values);
	jint len = (jint)size/2;
	result = env->NewIntArray(len * 2);
	jint fill[len * 2];
	//prefill the alreadyComputed
	for (int i = len; i < len * 2; i++)
	{
		fill[i] = -1;
	}

	jfloat* valuesNative = env->GetFloatArrayElements(values, 0);
	jint* alreadyComputedNative = env->GetIntArrayElements(alreadyComputed, 0);

	int internCount = -1;
	int index = 0;
	int indexForValues = 0;

	for (int j=startHeightIndex;j<endHeightIndex;j = j + delta)
	{
		for (int i=0;i<width;i = i + delta)
		{
			indexForValues = ((j - startHeightIndex) * width + i) * 2;
			index = ((j - startHeightIndex) * width + i);

			int returnValue = 0;

			if (alreadyComputedNative[index] != -1)
			{
				returnValue = alreadyComputedNative[index];
				//already computed in output
				fill[len + index] = returnValue;
			}
			else
			{
				unsigned int iterations;
			    unitMandelBrot(valuesNative, indexForValues, maxIter, internCount, interColorLength, returnValue, type);
				//already computed
				fill[len + index] = returnValue;
			}
			//fill the around points
			for (int l = 0; l < delta && i + l< width; l++)
			{
				for (int k = 0; k < delta && j + k < endHeightIndex; k++)
				{
					fill[index + l + k * width]  = returnValue;
				}

			}

		}
	}


	env->SetIntArrayRegion(result, 0, len * 2, fill);
	env->ReleaseIntArrayElements(result, fill, 0);
	return result;


}
